#pragma once

#include <string>

#include "arithmetic.h"
#include "symbol.h"
#include "types.h"

namespace IR {
    struct Operand {
        Type type;

        virtual void Print() const = 0;

        virtual Operand* Copy() const = 0;

        Operand(Type type) : type(type) {}
        Operand(Type::Basic base, bool isPtr) : type(base, isPtr) {}

        template <class T>
        T* As() {
            return dynamic_cast<T*>(this);
        }
    };

    struct Variable : public Operand {
        std::string name;
        bool isGlobal = false;
        Variable* memBase = nullptr;

        Variable() : Operand(Type::Basic::Void, false) {}
        Variable(Type type) : Operand(type) {}
        Variable(Type::Basic base, bool isPtr) : Operand(base, isPtr) {}

        void Print() const override;

        Operand* Copy() const override;
    };

    struct Immediate : public Operand {
        ArithValue value;

        Immediate(ArithValue value)
            : value(value), Operand(value.type, false) {}
        Immediate(int i) : value(i), Operand(Type::Basic::Int, false) {}
        Immediate(float f) : value(f), Operand(Type::Basic::Float, false) {}
        Immediate(Type::Basic type) : value(type), Operand(type, false) {}

        void Print() const override;

        Operand* Copy() const override;
    };

}  // namespace IR
